<?php

declare(strict_types=1);

namespace Zlf\AppValidate\Rule;

/**
 * 日期时间验证器
 *
 * 该类用于检查给定值是否为有效的日期时间，并且符合指定的时间格式。它继承自 RuleAbstract，
 * 并提供了设置错误信息、时间格式以及执行具体验证逻辑的方法。
 */
class DatetimeValidate extends RuleAbstract
{
    /**
     * 设置错误信息
     *
     * 当验证失败时显示的默认错误消息。可以包含占位符 `{label}`，将在实际使用时替换为字段标签。
     *
     * @var string
     */
    protected $_error = '{label}不是有效时间';

    /**
     * 时间格式验证
     *
     * 预期的日期时间格式。默认格式是 'Y-m-d H:i:s'，但可以通过 setFormat 方法进行修改。
     *
     * @var string
     */
    protected $_format = 'Y-m-d H:i:s';

    /**
     * 格式错误提示信息
     *
     * 当输入值不符合预期的时间格式时显示的错误消息。可以包含占位符 `{label}`。
     *
     * @var string
     */
    protected $_formatError = '{label}时间格式不符合要求';

    /**
     * 验证日期时间
     *
     * 如果输入值是一个字符串，则首先检查其是否为空。如果允许跳过空值并且值为空，则直接通过验证。
     * 然后尝试将字符串转换为时间戳，并与预期格式进行比较。如果匹配，则记录该值并返回 true；
     * 否则添加相应的错误信息并返回 false。
     *
     * @param mixed $value 待验证的值
     * @param string $label 字段标签，用于错误提示信息中
     * @return bool 验证结果：成功为 true，失败为 false
     */
    public function validate($value, string $label = ''): bool
    {
        if (is_string($value)) {
            // 如果允许跳过空值并且值为空，则直接通过验证
            if (empty($value) && $this->_noSkipping === true) {
                $this->_value = '';
                return true;
            }
            // 如果字符串长度为0，则直接通过验证
            if (strlen($value) === 0) {
                $this->_value = $value;
                return true;
            }
            // 尝试将字符串转换为时间戳
            $time = strtotime($value);
            if ($time === false) {
                return $this->addError($this->_error, $label);
            }
            // 检查转换后的值是否与预期格式匹配
            if ($value === date($this->_format, $time)) {
                $this->_value = $value;
                return true;
            }
            // 如果不匹配，添加格式错误信息
            return $this->addError($this->_formatError, $label);
        }
        // 如果输入值不是字符串，添加错误信息
        return $this->addError($this->_error, $label);
    }

    /**
     * 设置验证格式
     *
     * 允许调用者动态地设置或修改预期的日期时间格式。
     *
     * @param string $format 新的时间格式
     * @return $this 返回当前对象实例，便于链式调用
     */
    public function setFormat(string $format): DatetimeValidate
    {
        $this->_format = $format;
        return $this;
    }

    /**
     * 设置格式错误提示信息
     *
     * 允许调用者动态地设置或修改格式错误提示信息。
     *
     * @param string $error 新的格式错误提示信息
     * @return $this 返回当前对象实例，便于链式调用
     */
    public function setFormatError(string $error): DatetimeValidate
    {
        $this->_formatError = $error;
        return $this;
    }
}